using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using EPiServer.Common.Categories;
using EPiServer.Common.Queries;
using EPiServer.Common.Sorting;
using EPiServer.Common.Tags;
using EPiServer.Community.Blog;
using EPiServer.Community.Blog.Queries;
using JoelAbrahamsson.BlogExtensions;
using JoelAbrahamsson.Web.Common;
using JoelAbrahamsson.Web.ExtensionMethods;

namespace JoelAbrahamsson.Web.Controllers
{
    public class ArchiveController : Controller
    {
        private ExtendedBlogHandlerFacade _blogHandlerFacade;
        private BlogCategoryProvider _blogCategoryProvider;

        public ArchiveController(ExtendedBlogHandlerFacade blogHandlerFacade, BlogCategoryProvider blogCategoryProvider)
        {
            _blogHandlerFacade = blogHandlerFacade;
            _blogCategoryProvider = blogCategoryProvider;
        }

        public ActionResult Index()
        {
            List<LinkGroup> categoryGroups = GetCategoryGroups();
            ViewData["categories"] = categoryGroups;

            List<LinkGroup> monthGroups = GetMonthGroups();
            ViewData["months"] = monthGroups;

            return View();
        }

        private List<LinkGroup> GetMonthGroups()
        {
            List<LinkGroup> monthGroups = new List<LinkGroup>();
            DateTime firstMonth = GetFirstMonth();
            int year = firstMonth.Year;
            LinkGroup yearGroup = new LinkGroup { Name = firstMonth.Year.ToString() };
            yearGroup.Links = new List<Link>();
            monthGroups.Add(yearGroup);
            while (firstMonth.Subtract(new TimeSpan(firstMonth.Day, 0, 0, 0)) <= DateTime.Now)
            {
                yearGroup.Links.Add(GetMonthArchiveLink(firstMonth));
                firstMonth = firstMonth.AddMonths(1);
                if (firstMonth.Year > year)
                {
                    yearGroup = AddNewYearGroup(firstMonth, monthGroups);
                    year++;
                }
            }
            return monthGroups;
        }

        private LinkGroup AddNewYearGroup(DateTime firstMonth, List<LinkGroup> monthGroups)
        {
            LinkGroup yearGroup;
            yearGroup = new LinkGroup {Name = firstMonth.Year.ToString()};
            yearGroup.Links = new List<Link>();
            monthGroups.Add(yearGroup);
            return yearGroup;
        }

        private ArchiveLink GetMonthArchiveLink(DateTime firstMonth)
        {
           ArchiveLink link = new ArchiveLink
                {
                    Text = firstMonth.ToString("MMMM"), 
                    RouteValues = new RouteValueDictionary(
                        new
                           {
                               controller = "Archive", 
                               action = "month", 
                               year = firstMonth.Year, 
                               month = firstMonth.Month
                           })
                };

            int itemCount = GetEntryCount(firstMonth);

            link.ItemCount = itemCount;
            return link;
        }

        private int GetEntryCount(DateTime month)
        {
            EntryQuery query = new EntryQuery();
            
            query.PublicationStart = new DateTimeCriterion();
            query.PublicationStart.Value = month;
            query.PublicationStart.Operator = ComparisonOperator.GreaterThan;

            DateTime endDate = month.AddMonths(1);
            if (endDate > DateTime.Now)
                endDate = DateTime.Now;
            query.PublicationStart.Value2 = endDate;
            query.PublicationStart.Operator2 = ComparisonOperator.LessThan;

            int itemCount;
            _blogHandlerFacade.GetQueryResult(query, 1, 1, out itemCount);
            
            return itemCount;
        }

        private DateTime GetFirstMonth()
        {
            DateTime firstMonth = DateTime.Now;
            EntryQuery query = new EntryQuery();
            query.PublicationStart = new DateTimeCriterion();
            query.PublicationStart.Value = new DateTime(1900, 1, 1);
            query.PublicationStart.Operator = ComparisonOperator.GreaterThan;
            query.OrderBy.Add(new CriterionSortOrder(query.PublicationStart, SortingDirection.Ascending));
            EntryCollection entries = _blogHandlerFacade.GetQueryResult(query, 1, 1);
            if (entries.Count > 0)
                firstMonth = entries[0].PublicationStart;
            return new DateTime(firstMonth.Year, firstMonth.Month, 1);
        }

        private List<LinkGroup> GetCategoryGroups()
        {
            List<LinkGroup> categoryGroups = new List<LinkGroup>();
            foreach (ICategory mainCategory in _blogCategoryProvider.BlogCategoriesRoot.Children)
            {
                LinkGroup group = GetLinkGroup(mainCategory);
                categoryGroups.Add(group);
            }
            return categoryGroups;
        }

        private LinkGroup GetLinkGroup(ICategory mainCategory)
        {
            LinkGroup group = new LinkGroup
                                  {
                                      Name = mainCategory.Name,
                                      Links = new List<Link>()
                                  };
            foreach (ICategory childCategory in mainCategory.Children)
            {
                if(childCategory.GetEntryCount() == 0)
                    continue;

                ArchiveLink categoryLink = GetCategoryLink(mainCategory, childCategory);
                categoryLink.ItemCount = childCategory.GetEntryCount();

                group.Links.Add(categoryLink);
            }
            return group;
        }

        private ArchiveLink GetCategoryLink(ICategory mainCategory, ICategory childCategory)
        {
            ArchiveLink link = new ArchiveLink
                       {
                           Text = childCategory.Name,
                           RouteValues = new RouteValueDictionary(new
                                                                      {
                                                                          controller = "Archive",
                                                                          action = "Category",
                                                                          mainCategoryName = mainCategory.GetSlug(),
                                                                          subCategoryName = childCategory.GetSlug()
                                                                      })
                       };
            return link;
        }

        public ActionResult Category(ICategory category)
        {
            int totalItems;
            CategorizableEntityCollection entities = category.GetCategorizedItems(1, int.MaxValue - 1, out totalItems);
            IEnumerable<Entry> entriesInCategory = entities
                .Where(entity => entity is Entry)
                .Cast<Entry>()
                .Where(entry => entry.PublicationStart <= DateTime.Now);

            ViewData["mainCategory"] = category.Parent.Name;
            ViewData["subCategory"] = category.Name;

            return View(entriesInCategory);
        }

        public ActionResult Month(int year, int month)
        {
            DateTime dateInMonth = new DateTime(year, month, 1);
            ViewData["year"] = dateInMonth.ToString("yyyy");
            ViewData["month"] = dateInMonth.ToString("MMMM");

            IEnumerable<Entry> entriesFromMonth = _blogHandlerFacade.GetEntriesPublishedInMonth(year, month);

            return View(entriesFromMonth);
        }

        public ActionResult Tag(ITag tag)
        {
            int totalItems;
            IEnumerable<Entry> taggedEntries = tag.GetTaggedItems(typeof(Entry), 1, int.MaxValue - 1, out totalItems)
                .Cast<Entry>()
                .Where(entry => entry.PublicationStart <= DateTime.Now)
                .OrderByDescending(entry => entry.PublicationStart);

            ViewData["tagName"] = tag.Name;

            return View(taggedEntries);
        }
    }
}
